/*******************************************************************************
* Copyright (C) 2014 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*******************************************************************************
*/
/*============================================================================
 * 
 * Author(s): Maxim Integrated Products
 * Contributors:
 * Date:          
 * Purpose:       
 * Description:
 * 
 *==========================================================================*/
#include <errors.h>

#include <mml_sc.h>
#include <libemv.h>
#include <emv_config.h>
#include <emv_slot.h>


#define	STATE_CASE_1_START					0
#define	STATE_CASE_2_START					1
#define	STATE_CASE_3_START					2
#define	STATE_CASE_4_START					3
#define	STATE_CHECK_RESEND					4
#define	STATE_CHECK_WARNING					5
#define	STATE_CHAIN_RESPONSE				6

#define emv_get_sw1(_buf_)     (_buf_->data[_buf_->length - 2])
#define emv_get_sw2(_buf_)     (_buf_->data[_buf_->length - 1])

static const unsigned char emv_t0_get_response[5] = { 0,0xC0,0,0,0 };         
void emv_t0_handler(int event );



int emv_t0_transmit(emv_slot_t *slot)
{
    int						result;
//    emv_apdu_t				*r_apdu;
    emv_apdu_t				*c_apdu;
    emv_buf_t				*tx_buf;
    emv_buf_t				*rx_buf;


    /* Check if an operation is already pending */  
    if ( slot->status & EMV_STATE_OPERATION_PENDING )
    {
        result = COMMON_ERR_ALREADY;
        goto emv_t0_transmit_out;
    }

//    r_apdu = &slot->r_apdu;
    c_apdu = &slot->c_apdu;
    tx_buf = &slot->tx_buf;
    rx_buf = &slot->rx_buf;                        
    slot->status |= EMV_STATE_OPERATION_PENDING;
    /* Transfer C-APDU to the internal buffer */    
    tx_buf->length = *c_apdu->length; 
    emv_memcpy(tx_buf->data, c_apdu->data, tx_buf->length);
    /* register the handler to manage ATR events */ 
    mml_sc_attach(MML_SC_EVENT_DATA_SENT, emv_t0_handler);
    mml_sc_attach(MML_SC_EVENT_DATA_RECEIVED, emv_t0_handler);
    mml_sc_attach(MML_SC_EVENT_ERROR, emv_t0_handler);
	/* -> Determine the case number associated to the APDU length
    Case 1 : CLA INS P1 P2
    Case 2 : CLA INS P1 P2 Le
    Case 3 : CLA INS P1 P2 Lc [data]
    Case 4 : CLA INS P1 P2 Lc [data] Le
	*/
    switch( tx_buf->length )
    {
        case 4 :
            slot->state = STATE_CASE_1_START;
            /* Lc is equal to zero */
            tx_buf->data[4] = 0;
            break ;
        case 5 :
            /* case 2 */
            slot->state = STATE_CASE_2_START;
            break;
        default :
            if ( tx_buf->length == (unsigned int) (tx_buf->data[4] + 5) )
            {
                slot->state = STATE_CASE_3_START;
            }
            else
            {
                slot->state = STATE_CASE_4_START;
            }
    }
    /* Max received data :  256 data bytes + SW1/SW2 */
    rx_buf->length = EMV_CONFIG_APDU_MAX_SIZE; 
    slot->sw1 = 0;
    slot->sw2 = 0;
    /* Case 2 : CLA INS P1 P2 Le */
    if ( slot->state == STATE_CASE_2_START )
    {                                                
        slot->error = mml_sc_t0_out( tx_buf->data,
                                        rx_buf->data,
                                        &rx_buf->length);
    }
    else
    {
        slot->error = mml_sc_t0_in( tx_buf->data,
                                       rx_buf->data,
                                       &rx_buf->length);                                   
    }

	if ( slot->error != NO_ERROR )
	{
		slot->status &= ~EMV_STATE_OPERATION_PENDING;
	}

	result = slot->error;

emv_t0_transmit_out:
    return result;
}

void emv_t0_handler(int event)
{
    unsigned char			sw1;
    unsigned char			sw2;
    emv_slot_t				*slot = emv_get_slot();
    emv_apdu_t				*r_apdu;
//    emv_apdu_t				*c_apdu;
    emv_buf_t				*tx_buf;
    emv_buf_t				*rx_buf; 

	if ( slot )
	{
	    r_apdu = &slot->r_apdu;
//	    c_apdu = &slot->c_apdu;
	    tx_buf = &slot->tx_buf;
	    rx_buf = &slot->rx_buf;
	}
	else
	{	/* What an error ... */
		goto emv_t0_handler_out;
	}

    switch( event )
    {
        case MML_SC_EVENT_DATA_SENT:
            if ( slot->state == STATE_CASE_4_START )
            {
                slot->state = STATE_CHECK_WARNING;
            }
            else if ( slot->state == STATE_CASE_2_START )
            {
                slot->state = STATE_CHECK_RESEND;
            }
            else if ( ( slot->state == STATE_CASE_1_START )
					|| ( slot->state == STATE_CASE_3_START ) )
			{
                slot->state = STATE_CHAIN_RESPONSE;
			}

            emv_call_handler(slot, EMV_EVENT_DATA_SENT);
            break;
        case MML_SC_EVENT_DATA_RECEIVED:
            /* Update response APDU buffer */
            if ( r_apdu->alloc >= (*r_apdu->length + rx_buf->length) )
            {        
                emv_memcpy(r_apdu->data + *r_apdu->length, 
                        rx_buf->data, rx_buf->length); 
                *r_apdu->length += rx_buf->length; 
            }
            else
            {
                slot->error = N_SCS_ERR_NO_BUFS;
            }
            /* Retrieve the status words */
            sw1 = emv_get_sw1(rx_buf);
            sw2 = emv_get_sw2(rx_buf);
            /* Internal buffer length */
            rx_buf->length = emv_buffer_get_alloc(); 
            switch( slot->state )
            {
                case STATE_CASE_4_START:
	                if ( sw1 == 0x61 )
	                {
	                	slot->state = STATE_CHAIN_RESPONSE;
	                }
	                else
	                {
	                    if ( slot->sw1 )
	                    {
	                        r_apdu->data[*r_apdu->length - 2] = slot->sw1;
	                        r_apdu->data[*r_apdu->length - 1] = slot->sw2;
	                    }

	                    slot->status &= ~EMV_STATE_OPERATION_PENDING;
	                    emv_call_handler(slot, EMV_EVENT_DATA_RECEIVED);    
	                	break;	
	                }
                case STATE_CHECK_WARNING:
					/* Update state machine */
					slot->state = STATE_CHECK_RESEND;
					if ( ( sw1 == 0x62 ) ||
						( sw1 == 0x63 ) ||
						( ( ( sw1 & 0xF0 ) == 0x90 ) &&
							( ( sw1 != 0x90 ) || sw2 ) ) )
                        {
                            /* Store the status words */
                            slot->sw1 = sw1;
                            slot->sw2 = sw2;
                            /* Prepare GET RESPONSE C-APDU with Le = 0 */
                            emv_memcpy(tx_buf->data, (unsigned char*)emv_t0_get_response, 5);
                            slot->error = mml_sc_t0_out(   tx_buf->data,
                                                            rx_buf->data,
                                                            &rx_buf->length);
                            /* Remove previous status words */
                            if ( slot->error == NO_ERROR )
                            {
                                *r_apdu->length -= 2;
                            }
							else
							{
								goto end_error;
							}

                            goto emv_t0_handler_out;
                        }
				case STATE_CASE_2_START:
                case STATE_CHECK_RESEND:
                    /* Update state machine */
                    slot->state = STATE_CHAIN_RESPONSE;
                    if ( sw1 == 0x6c )
                    {
                        /* Prepare to resend the previous C-APDU */    
                        tx_buf->data[4] = sw2;  
                        slot->error = mml_sc_t0_out(       tx_buf->data,
                                                            rx_buf->data,
                                                            &rx_buf->length);
                        /* Remove previous status words */
                        if ( slot->error == NO_ERROR )
                        {
                            *r_apdu->length -= 2;
                        }
						else
						{
							goto end_error;
						}

                        goto emv_t0_handler_out;
                    }
                case STATE_CASE_1_START:
                case STATE_CASE_3_START:
                case STATE_CHAIN_RESPONSE:
                    if ( sw1 == 0x61 )
                    {
                        /* Prepare to send a GET RESPONSE command */
                        emv_memcpy(tx_buf->data, (unsigned char*)emv_t0_get_response, 4);
                        tx_buf->data[4] = sw2;  
                        slot->error = mml_sc_t0_out(       tx_buf->data,
                                                            rx_buf->data,
                                                            &rx_buf->length);
                        /* Remove previous status words */
                        if ( slot->error == NO_ERROR )
                        {
                            *r_apdu->length -= 2;
                        }
                        else
                        {
                        	goto end_error;
                        }

                        goto emv_t0_handler_out;
                    }
                default:
                    /* Change if needed the status words */
                    if ( slot->sw1 )
                    {
                        r_apdu->data[*r_apdu->length - 2] = slot->sw1;
                        r_apdu->data[*r_apdu->length - 1] = slot->sw2;                           
                    }
                                                                                             
                    slot->status &= ~EMV_STATE_OPERATION_PENDING;
                    emv_call_handler(slot, EMV_EVENT_DATA_RECEIVED);                   
            }      
            break;
        /* Error */               
        default:
            /* Retrieve the last error */
            mml_sc_get_last_error((int *) &slot->error);
end_error:
            slot->status &= ~EMV_STATE_OPERATION_PENDING;
            emv_call_handler(slot, EMV_EVENT_ERROR);
            break;
    }

emv_t0_handler_out:
	return;
}
